/*
	gtp-rhino.cc	GRhino GTP Frontend
	Copyright (c) 2005 Kriang Lerdsuwanakij
	email:		lerdsuwa@users.sourceforge.net

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "config.h"

#include "board.h"
#include "gtstream.h"
#include "gtp.h"

#include <iostream>
#include <exception>
#include <stdexcept>

#ifdef _
#undef _
#endif

#ifdef N_
#undef N_
#endif

#include <libintl.h>
#define _(x) gettext(x)
#define N_(x) x

void	input_char(char &c)
{
	std::cin.get(c);
	if (!std::cin)
		throw io_error();
}

void	output_char(char c)
{
	std::cout.put(c);
	if (!std::cout)
		throw io_error();
}

void	output_flush()
{
	std::cout << std::flush;
	if (!std::cout)
		throw io_error();
}

				// Input line ignoring newline
void	input_line(std::string &str)
{
				// Loop until line is neither empty
				// nor containing only white spaces
	for ( ; ; ) {
		str.erase();

				// Loop until we have a complete line
		for ( ; ; ) {
			char c;
			input_char(c);
			if (c == '#') {
				do {
					input_char(c);
				} while (c != '\n');
				break;		// Exit loop
			}
			else if (c == '\t')
				c = ' ';
			else if (c == '\n')
				break;		// Exit loop
			else if ((c >= 0 && c <= 31) || c == 127)
				continue;	// Ignore \r and
						// control chars

			str += c;
		}

		for (size_t i = 0; i < str.size(); ++i)
			if (!isspace(str[i]))
				return;		// Exit outer loop
	}
}

				// Output line togetherwith newline
void	output_line(const std::string &str)
{
	int found_newline = 0;
	for (size_t i = 0; i < str.size(); ++i) {
		output_char(str[i]);

		if (str[i] == '\n')
			found_newline++;
		else
			found_newline = 0;
	}

				// Output newline of necessary
	if (found_newline < 2)
		output_char('\n');
	if (found_newline < 1)
		output_char('\n');

	output_flush();
}

				// Output line togetherwith newline
void	output_line(const char *str)
{
	int found_newline = 0;
	while (*str) {
		output_char(*str);

		if (*str == '\n')
			found_newline++;
		else
			found_newline = 0;

		++str;
	}

				// Output newline of necessary
	if (found_newline < 2)
		output_char('\n');
	if (found_newline < 1)
		output_char('\n');

	output_flush();
}

size_t	parse_id(const std::string &str, bool &has_id, unsigned &id)
{
	size_t	i;
	bool	found_number = false;

	id = 0;

	for (i = 0; i < str.size(); ++i) {
		if (str[i] == ' ') {
			if (found_number) {
				has_id = true;
				return i;
			}
			else
				continue;
		}
		else if (str[i] >= '0' && str[i] <= '9') {
			id = id*10 + str[i]-'0';
			found_number = true;
		}
		else {
			if (found_number)	// Ex: 10x
				throw id_error();
			else {			// No id
				has_id = false;
				return i;
			}
		}
	}

	if (!found_number)		// Empty line
		throw id_error();

					// Reach here for line with
					// only id
	has_id = true;
	return i;
}

size_t	skip_space(const std::string &str, size_t i)
{
	while (i < str.size() && str[i] == ' ')
		++i;
	return i;
}

size_t	skip_non_space(const std::string &str, size_t i)
{
	while (i < str.size() && str[i] != ' ')
		++i;
	return i;
}

size_t	read_unsigned(const std::string &str, size_t i, unsigned &num)
{
	num = 0;

	if (i == str.size() || str[i] < '0' || str[i] > '9')
		throw syntax_error();

	while (i < str.size() && str[i] >= '0' && str[i] <= '9') {
		num = num*10 + str[i]-'0';
		++i;
	}

	if (i < str.size() && str[i] != ' ')
		throw syntax_error();

	return i;
}

size_t	read_float(const std::string &str, size_t i, double &num)
{
	num = 0.0;
	double mult = 1.0;
	bool found_decimal = false;

	if (i == str.size())
		throw syntax_error();
	if (str[i] == '+')
		++i;
	else if (str[i] == '-') {
		++i;
		mult *= -1.0;
	}

	if (i == str.size()
	    || ((str[i] < '0' || str[i] > '9') && str[i] != '.'))
		throw syntax_error();

	while (i < str.size()
	       && ((str[i] >= '0' && str[i] <= '9') || str[i] == '.')) {
		if (str[i] == '.') {
			if (found_decimal)
				throw syntax_error();
			found_decimal = true;
		}
		else {
			num = num*10 + str[i]-'0';
			if (found_decimal)
				mult /= 10.0;
		}
		++i;
	}

	if (i < str.size() && str[i] != ' ')
		throw syntax_error();

	num *= mult;
	return i;
}

size_t	read_color(const std::string &str, size_t i, int &color)
{
	size_t j = skip_non_space(str, i);
	if (j-i == 1 && toupper(str[i]) == 'B')
		color = BLACK;
	else if (j-i == 1 && toupper(str[i]) == 'W')
		color = WHITE;
	else if (j-i == 5 && toupper(str[i]) == 'B'
		 && toupper(str[i+1]) == 'L'
		 && toupper(str[i+2]) == 'A'
		 && toupper(str[i+3]) == 'C'
		 && toupper(str[i+4]) == 'K')
		color = BLACK;
	else if (j-i == 5 && toupper(str[i]) == 'W'
		 && toupper(str[i+1]) == 'H'
		 && toupper(str[i+2]) == 'I'
		 && toupper(str[i+3]) == 'T'
		 && toupper(str[i+4]) == 'E')
		color = WHITE;
	else
		throw syntax_error();

	return j;
}

void	throw_command_error_if_end_of_line(const std::string &str, size_t i)
{
	if (i == str.size())
		throw command_error();
}

void	throw_syntax_error_if_end_of_line(const std::string &str, size_t i)
{
	if (i == str.size())
		throw syntax_error();
}

void	throw_if_extra_argument(const std::string &str, size_t i)
{
	i = skip_space(str, i);
	if (i != str.size())
		throw syntax_error();
}

void	output_response(const std::string &str, bool has_id, unsigned id)
{
	gtstream bufstr;
	if (has_id) {
		if (str.size()) {
			gtout(bufstr, "=%$ %$") << id << str;
			output_line(bufstr.str());
		}
		else {
			gtout(bufstr, "=%$") << id;
			output_line(bufstr.str());
		}
	}
	else {
		if (str.size()) {
			gtout(bufstr, "= %$") << str;
			output_line(bufstr.str());
		}
		else {
			output_line("=");
		}
	}
}

void	output_error(const std::string &str, bool has_id, unsigned id)
{
	gtstream bufstr;
	if (has_id) {
		gtout(bufstr, "?%$ %$") << id << str;
		output_line(bufstr.str());
	}
	else {
		gtout(bufstr, "? %$") << str;
		output_line(bufstr.str());
	}
}

bool	process_unsigned_option(int argc, char *argv[], 
				const std::string &arg, int &i,
				const std::string &short_name,
				const std::string &long_name,
				unsigned min, unsigned max, unsigned &val)
{
	std::string long_name_eq = long_name;
	long_name_eq += '=';

	if (arg != short_name && arg != long_name
	    && arg.compare(0, long_name_eq.size(), long_name_eq))
		return false;

	std::string arg_name;
	std::string arg_val;

	if (!arg.compare(0, long_name_eq.size(), long_name_eq)) {
		arg_name = long_name;
		arg_val = arg.substr(long_name_eq.size());
		if (!arg_val.size()) {
			gtstream bufstr;
			gtout(bufstr, _("missing argument for option `%$\'")) <<  arg_name;
			throw std::runtime_error(bufstr.str());
		}
	}
	else {
		arg_name = arg;
		++i;
		if (i == argc) {
			gtstream bufstr;
			gtout(bufstr, _("missing argument for option `%$\'")) <<  arg_name;
			throw std::runtime_error(bufstr.str());
		}
		arg_val = argv[i];
	}

	unsigned new_val;
	try {
		size_t j = read_unsigned(arg_val, 0, new_val);
		throw_if_extra_argument(arg_val, j);
	}
	catch (...) {
		gtstream bufstr;
		gtout(bufstr, _("invalid argument for option `%$\'")) <<  arg_name;
		throw std::runtime_error(bufstr.str());
	}
	if (new_val < min || new_val > max) {
		gtstream bufstr;
		gtout(bufstr, _("invalid argument for option `%$\'")) <<  arg_name;
		throw std::runtime_error(bufstr.str());
	}

	val = new_val;
	return true;
}

